import {sync} from "fast-glob";
import {normalizePath} from "vite";
import {DefaultTheme} from "vitepress";
import * as fs from "fs";
import path from "path";

const matter = require("gray-matter")

/**
 * 页面数据
 */
declare interface PageData {
  src: string,
  // 请求路径
  url: string,
  // markdown frontmatter 参数
  frontmatter: { [key: string]: any },
  parent?: string,
  // 是否为层级目录
  isDirectory: boolean
}

/**
 * 导航栏数据
 */
declare interface Item {
  // 标题
  text?: string,
  // 跳转链接
  link?: string,
  // 激活
  activeMatch?: string,
  // 子级
  items?: Item[],
  // 父级
  parent?: string,
  // 是否折叠
  collapsed?: boolean,
}


/**
 * 解析 markdown
 * @param pattern 路径
 *
 * 1. 通过 glob 读取本地 markdown 文件, 用 fs 进行读取解析页面内容
 * 2. 通过本地路径及后缀文件截取,获取页面访问路径
 * 3. 借助 gray-matter 实现 markdown 文件 frontmatter 解析
 */
function parsePage(pattern: string): Array<PageData> {
  const row: Array<PageData> = []
  // 同步读取本地md文件
  sync(pattern, {ignore: ['**/node_modules/**', '**/dist/**']}).forEach(file => {
    const src: string = fs.readFileSync(file, 'utf-8')
    let url: string = '/' + normalizePath(path.relative('./docs/src', file)).replace(/\.md$/, '')
    build(src, url, row)
  })
  return row as Array<PageData>
}

/**
 * 组装数据
 * @param src 页面数据
 * @param url 请求地址
 * @param row 页面数据
 */
function build(src: string, url: string, row: Array<PageData>) {
  // 解析 frontmatter
  const {data: frontmatter} = matter(src)
  const flag = '/index'
  // 判断是否为目录
  const isDirectory = url.endsWith(flag);
  // 统一替换请求路径
  url = url.replace(flag, '');
  // 获取路径层级
  const pathArr: string[] = url.split('/');
  let parent: string = ''
  // 处理非根节点
  if (pathArr.length > 1) {
    pathArr.pop() // 移除数组最后元素
    parent = pathArr.join('/'); // 获取父级路径
    row.push({url, src, frontmatter, parent, isDirectory})
  }
  return row
}


/**
 * 预处理数据
 * @param pattern 资源读取路径
 */
function preProcess(pattern: string): Array<Item> {
  let sidebar: Array<Item> = []
  parsePage(pattern).sort((before: PageData, after: PageData) =>
    before.frontmatter.index - after.frontmatter.index
  ).forEach(data => {
    // 根据 index.md.md 排序,预处理数据,将扁平化数组存入map,为下一步循环做准备
    const link: string = data.url
    const isDirectory = data.isDirectory;
    // 组装数据缓存
    const item = {
      text: data.frontmatter.title,
      activeMatch: '^' + link,
      link: isDirectory ? link + '/' : link,
      parent: data.parent,
    }
    sidebar.push(item)
  })
  return sidebar as Array<Item>
}

/**
 * 组装导航栏参数
 * 1. 按照目录结构将页面组装为树形结构
 * 2. 根据是否最后层级,组装为对应的数据结构
 * 3. 填充页面标题并根据 frontmatter.index.md.md 进行排序
 * 4. 当页面包含子级时,添加可折叠属性
 */
export default (isSiderBar: boolean = false): Array<DefaultTheme.NavItem> => {
  // 读取数据并预处理
  const preData: Array<Item> = preProcess('./docs/src/**/*.md');
  // 组装树形结构返回
  return buildTree(preData, isSiderBar) as Array<DefaultTheme.NavItem>
}


/**
 * 将平级路径转为树形结构
 * @param items 扁平数组
 * @param isSiderBar 是否侧边栏
 */
function buildTree(items: Array<Item>, isSiderBar: boolean = false) {
  let findItem: Item | undefined = {}
  const tree = items.reduce((previous, current) => {
    // 获取父节点
    const parent = current.parent;
    // 存入根路径
    if (parent == '') {
      previous.push(current);
      return previous;
    }
    findItem = items.find(it => it.activeMatch?.replace('^', '') == parent)
    if (findItem) { // 存在 children
      if (isSiderBar) {
        findItem.collapsed = false
        findItem.link = undefined
        findItem.items?.length ? findItem.items.push(current) : findItem.items = [current];
        return previous
      }
      // navbar 最多三级 NavItemWithChildren -> [NavItemChildren] -> NavItemWithLink
      const level = findItem.activeMatch?.split('/').length;
      if (level && level <= 3) {
        findItem.link = undefined
        findItem.items?.length ? findItem.items.push(current) : findItem.items = [current];
        return previous
      }
    }
    return previous;
  }, new Array<Item>);
  if (isSiderBar) {
    let sideBar: Record<string, Array<Item>> = {}
    tree.forEach(data => {
      const replace = data.activeMatch?.replace('^', '');
      if (data.items && replace) {
        sideBar[replace] = data.items
      }
    })
    return sideBar
  }
  return tree
}
